package com.cm.etax.sms;

import com.cm.etax.sms.bean.SmsResult;
import com.cm.etax.sms.exceptions.SmsException;
import com.cm.kits.HttpTool;
import com.cm.kits.JacksonKits;
import com.cm.kits.YamlKits;
import lombok.Data;
import org.apache.commons.lang3.tuple.Pair;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

/**
 * 短信管理服务
 *
 * @author LWang 2023.01.30
 * @since 1.0.0
 */
public final class SmsBiz {
    private SmsBiz() {

    }

    /**
     * 短信处理配置类
     *
     * @author LWang 2023.02.01
     * @since 1.0.0
     */
    @Data
    public static class SmsConfig {

        /**
         * 短信服务地址
         */
        private String url;
    }

    /**
     * 短信发送处理接口
     */
    public interface SendShortMessageExecutor {
        /**
         * 发送短信功能
         */
        void execute();
    }

    /**
     * 短信类型枚举
     *
     * @author LWang 2023.01.30
     * @since 1.0.0
     */
    public enum SmsTaskType {
        /**
         * 天津市电子税务局验证码短信
         */
        ETAX_TIANJIN("TJ_ETAX"),
        /**
         * 测试类型的短信
         */
        TEST("TEST");
        final String type;

        public String getType() {
            return type;
        }

        SmsTaskType(String type) {
            this.type = type;
        }
    }

    private final static SmsConfig CONFIG;

    private final static HttpTool HTTP_TOOL;

    static {
        CONFIG = YamlKits.load("sms.yaml", SmsConfig.class);
        try {
            HTTP_TOOL = HttpTool.build();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 添加读取短信任务
     *
     * @param phone 手机号
     * @return 添加结果
     */
    private static SmsResult addTask(String phone, SmsTaskType type) throws IOException, InterruptedException {
        var url = String.format("%s/monitor_task/add", CONFIG.url);
        var response = HTTP_TOOL.post(url, new ArrayList<>() {{
            this.add(Pair.of("data", String.format("""
                            {
                                "phone": "%s",
                                "taskType": "%s"
                            }
                            """, phone, type.getType()
                    )
            ));
        }});
        if (response.getKey() != 200) {
            return new SmsResult(SmsResult.UNKNOWN, "网络错误！", null);
        }
        return JacksonKits.toBean(response.getValue(), SmsResult.class);
    }

    /**
     * 读取短信任务结果
     *
     * @param taskId 任务 ID
     * @return 读取结果
     */
    private static SmsResult readTask(String taskId) throws IOException, InterruptedException {
        var url = String.format("%s/monitor_task/query", CONFIG.url);
        var response = HTTP_TOOL.post(url, new ArrayList<>() {{
            this.add(Pair.of("task_id", taskId));
        }});
        if (response.getKey() != 200) {
            return new SmsResult(SmsResult.UNKNOWN, "网络错误！", null);
        }
        return JacksonKits.toBean(response.getValue(), SmsResult.class);
    }

    /**
     * 取消短信任务
     *
     * @param taskId 任务 ID
     * @return 读取结果
     */
    private static SmsResult cancelTask(String taskId) throws IOException, InterruptedException {
        var url = String.format("%s/monitor_task/cancel", CONFIG.url);
        var response = HTTP_TOOL.post(url, new ArrayList<>() {{
            this.add(Pair.of("task_id", taskId));
        }});
        if (response.getKey() != 200) {
            return new SmsResult(SmsResult.UNKNOWN, "网络错误！", null);
        }
        return JacksonKits.toBean(response.getValue(), SmsResult.class);
    }

    /**
     * 等待验证码
     *
     * @param phone    手机号
     * @param type     验证码类型
     * @param executor 执行器
     * @return 验证码
     */
    public static String waitSmsCode(String phone, SmsTaskType type, SendShortMessageExecutor executor) {
        try {
            var taskId = SmsResult.handle(addTask(phone, type)).data().taskId();
            try {
                executor.execute();
                var queryResult = SmsResult.handle(readTask(taskId));
                var retryTimes = 12;
                while (queryResult.code() == SmsResult.PROCESSING) {
                    queryResult = SmsResult.handle(readTask(taskId));
                    if (retryTimes-- <= 0) {
                        throw new SmsException(SmsException.NET_ERROR, "获取验证码超时！");
                    }
                    try {
                        TimeUnit.SECONDS.sleep(5);
                    } catch (InterruptedException ignore) {
                    }

                }
                return queryResult.data().validateCode();
            } finally {
                cancelTask(taskId);
            }
        } catch (IOException | InterruptedException e) {
            throw new SmsException(SmsException.NET_ERROR, "短信服务异常，任务无法继续！");
        }
    }

    public static void main(String[] args) {
        waitSmsCode("18920961634", SmsTaskType.TEST, () -> System.out.println("发短信喽……"));
    }
}
